﻿#pragma once
#include "LuaCommon.h"
#include "LuaObject.h"
#include "LuaStackOps.h"
#include "LuaException.h"
#include <array>


//调用lua函数使用,如果脚本错误会抛出C++异常
class LUAOBJ_API LuaFunctionVoid :public LuaObject
{
public:
	LuaFunctionVoid(const LuaObject& rfs)
		:LuaObject(rfs) {
	}

	LuaFunctionVoid(const LuaFunctionVoid& rfs)
		:LuaObject(rfs) {
	}

	inline bool isValid() const {
		return getType() == LUA_TFUNCTION;
	}

	template <class... T>
	void operator()(T... args)
	{
		if (!isValid()) {
			LUA_THROW_EXCEPTION("attempt to call a invalid function");
		}
		lua_State* L = getLuaState();
		lua_pushvalue(L, getIndex());
		StackOps::PushMulti(L, args...);
		if (lua_pcall(L, sizeof...(args), 0, 0)) {
			std::string error = lua_tostring(L, -1);
			lua_pop(L, 1);
			LUA_THROW_EXCEPTION(error.c_str());
		}
	}
};

//返回一个参数
class LUAOBJ_API LuaFunction :public LuaObject
{
public:
	LuaFunction(const LuaObject& rfs) 
		:LuaObject(rfs){
	}

	LuaFunction(const LuaFunction& rfs) 
		:LuaObject(rfs){
	}

	inline bool isValid() const {
		return getType() == LUA_TFUNCTION;
	}

	template <class... T>
	LuaObject operator()(T... args)
	{
		if (!isValid()) {
			LUA_THROW_EXCEPTION("attempt to call a invalid function");
			return luaNil;
		}
		lua_State* L = getLuaState();
		lua_pushvalue(L, getIndex());
		StackOps::PushMulti(L, args...);
		if (lua_pcall(L, sizeof...(args), 1, 0)) {
			std::string error = lua_tostring(L, -1);
			lua_pop(L, 1);
			LUA_THROW_EXCEPTION(error.c_str());
			return luaNil;
		} else {
			return LuaObject(getCppLuaState(), lua_gettop(L));
		}
	}

};

//返回多个参数
template<size_t N>
class LuaFunctionN:public LuaObject {
public:
	LuaFunctionN(const LuaObject& rfs)
		:LuaObject(rfs) {
	}

	LuaFunctionN(const LuaFunction& rfs)
		:LuaObject(rfs) {
	}

	inline bool isValid() const {
		return getType() == LUA_TFUNCTION;
	}

	template <class... T>
	std::array<LuaObject,N> operator()(T... args)
	{
		std::array<LuaObject, N> rtn;
		if (!isValid()) {
			LUA_THROW_EXCEPTION("attempt to call a invalid function");
			return rtn;
		}
		lua_State* L = getLuaState();
		lua_pushvalue(L, getIndex());
		StackOps::PushMulti(L, args...);
		if (lua_pcall(L, sizeof...(args), N, 0)) {
			std::string error = lua_tostring(L, -1);
			lua_pop(L, 1);
			LUA_THROW_EXCEPTION(error.c_str());
			return rtn;
		}
		else {
			int top = lua_gettop(L);
			for (size_t i = 0; i < N; ++i) {
				rtn[i] = LuaObject(getCppLuaState(), top - N + i + 1);
			}
			return rtn;
		}
	}
};

